/*
 *  Theory0039.cpp
 *  SoundController0002
 *
 *  Created by jiro on 11/07/17.
 *  Copyright 2011 jiro music. All rights reserved.
 *
 */

#include "Theory0039.h"

#include "../../sc/AllExceptions.h"
#include "../../sc/Common/Note.h"
#include "../HarmonicSystem/Sound.h"
#include "../HarmonicSystem/HarmonicSound.h"
#include <vector>
#include <set>
#include <map>

namespace theory0039
{
	const char* Theory::className_1_1 = "1-1";

	const char* Theory::className_1_2 = "1-2";

	const char* Theory::className_2_1_1 = "2-1-1";

	const char* Theory::className_2_1_2 = "2-1-2";

	const char* Theory::className_2_2 = "2-2";

	void Theory::ClassifyChord(
		const std::vector< SingleGroupGenerator >& generators, 
		const std::vector< sc::Note::Id >& startNotes,
		ClassifiedChords& classifiedChords )
	{
		if ( generators.size() != startNotes.size() )
		{
			throw sc::InvalidParameterException( 
			"void Theory::ClassifyChord(\n"
			"\tconst std::vector< SingleGroupGenerator >& generators,\n"
			"\tconst std::vector< sc::Note::Id >& startNotes,\n"
			"\tClassifiedChords& classifiedChords )" );
		}

		classifiedChords.Clear();

		//	set class id
		classifiedChords.AddId( Theory::className_1_1 );
		classifiedChords.AddId( Theory::className_1_2 );
		classifiedChords.AddId( Theory::className_2_1_1 );
		classifiedChords.AddId( Theory::className_2_1_2 );
		classifiedChords.AddId( Theory::className_2_2 );

		//	calc note array for each part
		int partCount = generators.size();
		std::vector< Chord > generateChords( partCount );
		for ( int i = 0; i < partCount; i++ )
		{
			const SingleGroupGenerator& generator = generators[ i ];
			const sc::Note::Id startNote = startNotes[ i ];
			generateChords[ i ] = generator.GenerateChord( startNote );
		}

		//	 calc all chords generated by generateChords
		int chordCount = 1;
		for ( int i = 0; i < partCount; i++ )
		{
			chordCount *= generateChords[ i ].GetNoteCount();
		}

		//	calc all chords
		int repeatCount = chordCount;
		std::vector< Chord > chords( chordCount );
		for ( int i = 0; i < partCount; i++ )
		{
			const Chord& chord = generateChords[ i ];
			int noteCount =  chord.GetNoteCount();
			repeatCount = repeatCount / noteCount;
			int index = 0;
			for ( int l = 0; l < chordCount / ( noteCount * repeatCount ); l++ )
			{
				for ( int j = 0; j < chord.GetNoteCount(); j++ )
				{
					sc::Note::Id noteId = chord.GetNoteIdOfIndex( j );
					for ( int k = 0; k < repeatCount; k++ )
					{
						chords[ index++ ].AddNoteId( noteId );
					}
				}
			}
		}

		//	classify
		for ( int j = 0; j < chordCount; j++ )
		{
			Chord& chord = chords[ j ];
			int intervalCount = chord.GetIntervalCount();
			std::vector< int > generateChordIndices;
			std::string className;

//			std::cout << chord.ToString() << std::endl;

			//	class "1-1" or "1-2"
			for ( int i = 0; i < generateChords.size(); i++ )
			{
				const Chord& generateChord = generateChords[ i ];
				bool isInClass = true;
				for ( int k = 0; k < intervalCount; k++ )
				{
					sc::Note::Interval interval = chord.GetIntervalOfIndex( k );
					if ( interval == sc::Note::Perfect1st ) 
					{
						continue;
					}
					if ( ! generateChord.IsIncludeInterval( interval ) )
					{
						isInClass = false;
						break;
					}
				}
				if ( isInClass )
				{
					generateChordIndices.push_back( i );
					chord.AddGenerator( generators[ i ], startNotes[ i ] );
				}
			}
			if ( ! generateChordIndices.empty() )
			{
				if ( generateChordIndices.size() == 1 )
				{
					className = className_1_1;
				}
				else if ( generateChordIndices.size() > 1 )
				{
					className = className_1_2;
				}
				classifiedChords.AddChord( className, chord );
				continue;
			}
			
			//	class "2-1-1", "2-1-2"
			for ( int i = 0; i < generateChords.size(); i++ )
			{
				const Chord& generateChord = generateChords[ i ];
				bool isShearInterval = false;
				for ( int k = 0; k < intervalCount; k++ )
				{
					sc::Note::Interval interval = chord.GetIntervalOfIndex( k );
					if ( interval == sc::Note::Perfect1st )
					{
						continue;
					}
					if ( generateChord.IsIncludeInterval( interval ) )
					{
						isShearInterval = true;
						break;
					}
				}
				if ( isShearInterval )
				{
					generateChordIndices.push_back( i );
					chord.AddGenerator( generators[ i ], startNotes[ i ] );
				}
			}
			if ( ! generateChordIndices.empty() )
			{
				if ( generateChordIndices.size() == 1 )
				{
					className = className_2_1_1;
				}
				else 
				{
					className = className_2_1_2;
				}
				classifiedChords.AddChord( className, chord );
				continue;
			}

			//	class "2-2"
			classifiedChords.AddChord( className_2_2, chord );
		}
		
		//	sort by chord type
		for ( int i = 0; i < classifiedChords.GetClassCount(); i++ )
		{
			const std::vector< Chord >& chords = classifiedChords.GetChords( i );
			std::multimap< std::set< int >, Chord > sortedChordsList;
			std::set< std::set< int > > keyPatterns;
			for ( int j = 0; j < chords.size(); j++ )
			{
				keyPatterns.insert( chords[ j ].GetIntervalPattern() );
				sortedChordsList.insert( std::pair< std::set< int >, Chord >( chords[ j ].GetIntervalPattern(), chords[ j ] ) );
			}

			std::vector< Chord > sortedChords;
			for ( std::set< std::set< int > >::iterator keyIter = keyPatterns.begin(); keyIter != keyPatterns.end(); keyIter++ )
			{
				std::pair< std::multimap< std::set< int >, Chord >::iterator, 
						   std::multimap< std::set< int >, Chord >::iterator > iterPair;
				iterPair = sortedChordsList.equal_range( *keyIter );
				std::multimap< std::set< int >, Chord >::iterator iter;
				for ( iter = iterPair.first; iter != iterPair.second; iter++ )
				{
					sortedChords.push_back( iter->second );

//					std::cout << iter->second.ToString() << " ";
//					std::set< int >::iterator iter000 = iter->first.begin();
//					for ( ; iter000 != iter->first.end(); iter000++ )
//					{
//						std::cout << *iter000 << " ";
//					}
//					std::cout << std::endl;
				}
			}

			classifiedChords.SetChords( i, sortedChords );
		}
	}
	
	bool Theory::IsContain( const std::vector< Chord >& chords, const Chord& chord )
	{
		for ( int i = 0; i < chords.size(); i++ )
		{
			if ( chords[ i ] == chord )
			{
				return true;
			}
		}
		return false;
	}

	void Theory::Sort( const sc::hs::Sound::NearbyWellTemperamentNotes& notes, std::vector< sc::hs::Sound::NearbyWellTemperamentNotes >& sortedNotesList )
	{
		sortedNotesList.clear();
		sortedNotesList.resize( sc::Note::NoteIdCount );
		sc::hs::Sound::NearbyWellTemperamentNotes::const_iterator iter = notes.begin();
		for ( ; iter != notes.end(); iter++ )
		{
			for ( int i = 0; i < sc::Note::NoteIdCount; i++ )
			{
				sc::Note::MidiNumber noteNumber = static_cast< sc::Note::MidiNumber >( i );
				if ( noteNumber != ( iter->second % sc::Note::NoteIdCount )  )
				{
					continue;
				}
				sortedNotesList[ sc::Note::GetNoteIdFrom( noteNumber ) ].insert( sc::hs::Sound::NearbyWellTemperamentNotePair( iter->first, iter->second ) );
				break;
			}
		}
	}
}
